package mapper

import (
	"context"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	constans "mall/services/product/rpc/consts"
	"mall/services/product/rpc/internal/domain/bo"
	"mall/services/product/rpc/internal/initialize/mysql"
	"mall/services/product/rpc/internal/mapper/entity"
	"strings"
	"time"
)

var spuAllColumn = "spu_id, create_time, update_time, brand_id, brand_name, category_id, category_name, " +
	"shop_category_id, shop_category_name, shop_id, shop_name, name, selling_point, main_img_url, " +
	"img_urls, video, price_fee, market_price_fee, status, has_sku_img, seq"

type SpuMapper[T comparable] struct {
}

func NewSpuMapper[T comparable]() *SpuMapper[T] {
	return &SpuMapper[T]{}
}

func (m *SpuMapper[T]) SelectCountCtx(ctx context.Context, db *gorm.DB, spuBO bo.SpuBO) (totalCount int64, err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#SelectCountCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	db = db.Session(&gorm.Session{SkipDefaultTransaction: true})
	var total int64
	userEntity := entity.SpuEntity{}
	db = db.Select("count(spu_id)").Table(userEntity.TableName())
	m.getAllWhereSql(db, spuBO)
	db = db.Count(&total)
	return total, db.Error
}

func (m *SpuMapper[T]) SelectOneCtx(ctx context.Context, db *gorm.DB, spuBO bo.SpuBO) (spuEntity entity.SpuEntity, err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#SelectOneCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	tx := db.Session(&gorm.Session{SkipDefaultTransaction: true})
	var resp entity.SpuEntity
	userEntity := entity.SpuEntity{}
	tx = tx.Select(spuAllColumn).Table(userEntity.TableName())
	m.getAllWhereSql(tx, spuBO)
	tx = tx.First(&resp)
	return resp, tx.Error
}

func (m *SpuMapper[T]) SelectListCtx(ctx context.Context, db *gorm.DB, spuBO bo.SpuBO) (spuEntity []entity.SpuEntity, err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#SelectListCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	tx := db.Session(&gorm.Session{SkipDefaultTransaction: true})
	var resp []entity.SpuEntity
	userEntity := entity.SpuEntity{}
	tx = tx.Select(spuAllColumn).Table(userEntity.TableName())
	m.getAllWhereSql(tx, spuBO)
	tx = tx.Find(&resp)
	return resp, tx.Error
}

func (m *SpuMapper[T]) Insert(ctx context.Context, db *gorm.DB, spuEntity entity.SpuEntity) (spuId int64, err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#Insert")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	tx := db.Create(&spuEntity)
	return spuEntity.SpuId, tx.Error
}

func (m *SpuMapper[T]) getAllWhereSql(db *gorm.DB, bo bo.SpuBO) {
	if bo.SpuId > 0 {
		db.Where("spu_id = ?", bo.SpuId)
	}
	if len(bo.StartTime) > 0 || strings.TrimSpace(bo.StartTime) != "" {
		db.Where("create_time = ?", bo.StartTime)
	}
	if len(bo.EndTime) > 0 || strings.TrimSpace(bo.EndTime) != "" {
		db.Where("create_time = ?", bo.EndTime)
	}
	if bo.BrandId > 0 {
		db.Where("brand_id = ?", bo.BrandId)
	}
	if len(bo.BrandName) > 0 || strings.TrimSpace(bo.Name) != "" {
		db.Where("brand_name = ?", bo.BrandName)
	}
	if bo.CategoryId > 0 {
		db.Where("category_id = ?", bo.CategoryId)
	}
	if len(bo.CategoryName) > 0 || strings.TrimSpace(bo.CategoryName) != "" {
		db.Where("category_name = ?", bo.CategoryName)
	}
	if bo.ShopCategoryId > 0 {
		db.Where("shop_category_id = ?", bo.ShopCategoryId)
	}
	if len(bo.ShopCategoryName) > 0 || strings.TrimSpace(bo.ShopCategoryName) != "" {
		db.Where("shop_category_name = ?", bo.ShopCategoryName)
	}
	if bo.ShopId > 0 {
		db.Where("shop_id = ?", bo.ShopId)
	}
	if len(bo.ShopName) > 0 || strings.TrimSpace(bo.ShopName) != "" {
		db.Where("shop_name = ?", bo.ShopName)
	}
	if len(bo.Name) > 0 || bo.Name != "" {
		db.Where("name = ?", bo.Name)
	}
	if len(bo.NameAllLike) > 0 || bo.NameAllLike != "" {
		db.Where("name like ?", "%"+bo.Name+"%")
	}
	if len(bo.SellingPoint) > 0 || bo.SellingPoint != "" {
		db.Where("selling_point = ?", bo.SellingPoint)
	}
	if len(bo.MainImgUrl) > 0 || bo.MainImgUrl != "" {
		db.Where("main_img_url = ?", bo.MainImgUrl)
	}
	if len(bo.ImgUrls) > 0 || bo.ImgUrls != "" {
		db.Where("img_urls = ?", bo.ImgUrls)
	}
	if bo.PriceFee > 0 {
		db.Where("price_fee = ?", bo.PriceFee)
	}
	if bo.MarketPriceFee > 0 {
		db.Where("market_price_fee = ?", bo.MarketPriceFee)
	}
	if bo.Status != 0 {
		db.Where("status = ?", bo.Status)
	}
	if bo.NeStatus != 0 {
		db.Where("status != ?", bo.NeStatus)
	}
	if bo.HasSkuImg > 0 {
		db.Where("has_sku_img = ?", bo.HasSkuImg)
	}
	if bo.Seq > 0 {
		db.Where("seq = ?", bo.Seq)
	}
	if len(bo.SpuIdArr) > 0 {
		db.Where("spu_id in ?", bo.SpuIdArr)
	}
}

func (m *SpuMapper[T]) SelectPageListCtx(ctx context.Context, db *gorm.DB, spuBO bo.SpuBO) (returnArr []entity.SpuEntity, err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#SelectPageListCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	tx := db.Session(&gorm.Session{SkipDefaultTransaction: true})
	spuEntity := entity.SpuEntity{}

	// query sql
	var spuEntityArr []entity.SpuEntity
	if spuBO.PageSize > 0 {
		tx = tx.Select(spuAllColumn).Table(spuEntity.TableName()).Offset(spuBO.StartIndex)
		m.builderWhereAndSorts(tx, spuBO, spuEntity)
		tx = tx.Limit(spuBO.PageSize).Find(&spuEntityArr)
	} else {
		tx.Select(spuAllColumn).Table(spuEntity.TableName())
		m.builderWhereAndSorts(tx, spuBO, spuEntity)
		tx = tx.Find(&spuEntityArr)
	}
	// return obj
	return spuEntityArr, nil
}

func (m *SpuMapper[T]) builderWhereAndSorts(tx *gorm.DB, spuBO bo.SpuBO, spuEntity entity.SpuEntity) {
	// builder where
	m.getAllWhereSql(tx, spuBO)
	// process order
	if len(spuBO.Sorts) > 0 {
		for _, v := range spuBO.Sorts {
			orderByColumn := clause.OrderByColumn{}
			orderByColumn.Column = clause.Column{Name: v.FiledName, Table: spuEntity.TableName()}
			if v.Sort == "asc" {
				orderByColumn.Desc = false
			} else {
				orderByColumn.Desc = true
			}
			tx.Order(orderByColumn)
		}
	}
}

func (m *SpuMapper[T]) UpdateCtx(ctx context.Context, engin *gorm.DB, spuEntity entity.SpuEntity) (err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#UpdateCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	tx := engin.Where("spu_id = ?", spuEntity.SpuId).Omit("spu_id").
		UpdateColumns(spuEntity)
	return tx.Error
}

func (m *SpuMapper[T]) DelSpuCtx(ctx context.Context, engin *gorm.DB, spuIdArr []int64) (err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#DelSpuCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	tx := engin.Table("spu").Where("spu_id in ?", spuIdArr).Omit("spu_id").
		Update("status", constans.SpuTakeDown).Update("update_time", time.Now())
	return tx.Error
}

func (m *SpuMapper[T]) SpuManageCtx(ctx context.Context, engin *gorm.DB, spuIdList []int64, status int) (err error) {
	// 监控
	ctx, span := mysql.StartSpan(ctx, "SpuMapper#SpuManageCtx")
	defer func() {
		mysql.EndSpan(span, err)
	}()
	if len(spuIdList) <= 0 {
		// 不存在直接返回
		return nil
	}
	spuEntity := entity.SpuEntity{}
	tx := engin.Table(spuEntity.TableName()).Where("spu_id in ?", spuIdList).
		Omit("spu_id").Omit("spu_id").Update("status", status).
		Update("update_time", time.Now())
	return tx.Error
}
